package com.sinai.mshab.client.ui.basic;

import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FormHandler;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormSubmitEvent;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.util.Util;
import com.sinai.mshab.server.RequestEntry;
import com.sinai.mshab.server.RequestPOSTEntry;
import com.sinai.mshab.client.ui.aggregate.DataReceiver;
import com.sinai.mshab.client.ui.aggregate.FormData;

/**
 * mshab implementation of {@link FileUpload}. Like all mshab widgets, this
 * widget includes description, quantifier and error labels. Unlike other mshab
 * widgets, the FileUploadComponent requires a separate servlet in order to
 * operate. The {@link FileUploadServlet} needs to be deployed under Tomcat as a
 * servlet service named <i>mshabFileUploadServlet</i>. This is required
 * because at the moment, it is not possible to send files to the server using
 * RPC calls. Therefore, an HTML Form that uses a POST is utilized.
 * 
 * FileUploadComponent avoids reload the entire Page by placing itself in a
 * {@link Frame}. By doing this, only the FileUploadComponent's {@link Frame}
 * is reloaded while the rest of the Page is not.
 * 
 * @author Arthur Kalmenson
 * 
 */
public class FileUploadComponent extends BasicFormComponent {

	private static final String MSH_FILE_UPLOAD_SERVLET_URL = "/mshabFileUploadServlet";

	private static final String MSH_FILE_DOWNLOAD_SERVLET_URL = "/mshabFileDownloadServlet";

	private FormPanel uploadForm;

	private FileUpload upload;

	private String fileDBFieldName;

	private String fileTableName;

	public FileUploadComponent(String fileNameDBFieldName,
			String fileNameTableName, String fileDBFieldName,
			String fileTableName, String description) {
		initializeWidget(fileNameDBFieldName, fileNameTableName,
				fileDBFieldName, fileTableName, description, null, null, true);
	}

	protected void initializeWidget(String fileNameDBFieldName,
			String fileNameTableName, String fileDBFieldName,
			String fileTableName, String description, String quantifier,
			String error_msg, boolean horizontal) {

		this.fileDBFieldName = fileDBFieldName;
		this.fileTableName = fileTableName;

		// Create a FormPanel and point it at a service.
		uploadForm = new FormPanel();
		uploadForm.setAction(MSH_FILE_UPLOAD_SERVLET_URL);

		// Because we're going to add a FileUpload widget, we'll need to set the
		// form to use the POST method, and multipart MIME encoding.
		uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
		uploadForm.setMethod(FormPanel.METHOD_POST);

		// Create a FileUpload widget.
		upload = new FileUpload();
		upload.setName("uploadFormElement");

		uploadForm.setWidget(upload);

		// Add an event handler to the form.
		uploadForm.addFormHandler(new UploadFormHandler());

		initialize(fileNameDBFieldName, fileNameTableName, description,
				quantifier, error_msg, uploadForm, horizontal);
	}

	public void sendAnswer(DataReceiver receiver) {

		// send the filename over.
		receiver.sendData(getAnswer());

		// submit the form so the servlet can upload the file.
		if (upload.getFilename() != "") {
			uploadForm.submit();
		}
	}

	public Answer getAnswer() {
		Answer answer = new Answer();
		answer.add(dbfieldName, tableName, upload.getFilename());
		answer.add(hiddenFields);
		return answer;
	}

	public Object clone() {
		return null;
	}

	// public boolean populate() {
	// Answer a = FormData.getData(tableName);
	// return populate(a);
	// }
	//	
	// public boolean populate(Answer answer) {
	//
	// boolean result = false;
	//		
	// // if answer is null, do nothing
	// if (answer == null) {
	// return result;
	// }
	//		
	// String value = answer.get(dbfieldName);
	//
	// // if we populated the widget with nothing, return false.
	// if (value != null && value.trim().length() > 0) {
	// ((FileUpload) widget).getElement();
	// }
	//		
	// return result;
	// }

	public HorizontalPanel toReport() {

		HorizontalPanel result = new HorizontalPanel();

		// make the request builder that'll request the
		String request = Util.buildQueryString(buildDownloadRequest());

		// get the file path and extract the file name.
		String filePath = FormData.getData(tableName).get(dbfieldName);
		String fileName = Util.parseFileName(filePath);
		
		// link to the servlet
		HTML link = new HTML("<a href=\"" + MSH_FILE_DOWNLOAD_SERVLET_URL + "?"
				+ request + "\">Click here to get " + fileName + "</a>");
		link.setStyleName(ANSWER_LABEL_STYLE);

		// add the various labels
		result.add(link);

		return result;
	}

	public boolean isEmpty() {
		return upload.getFilename() == "";
	}

	private RequestEntry[] buildDownloadRequest() {

		// the keys
		HashMap keys = FormData.getKey().getKeys();

		// we need 2 database fields and 2 table names as well as all the keys.
		RequestEntry[] attributes = new RequestPOSTEntry[4 + keys.size()];

		// fill it up with the database information.
		attributes[0] = new RequestPOSTEntry("fileNameFieldName", dbfieldName);
		attributes[1] = new RequestPOSTEntry("fileNameTableName", tableName);
		attributes[2] = new RequestPOSTEntry("fileFieldName", fileDBFieldName);
		attributes[3] = new RequestPOSTEntry("fileTableName", fileTableName);

		// now fill the attributes up with the key information
		int attIndex = 4;
		int index = 0;
		Iterator iter = keys.keySet().iterator();
		while (iter.hasNext()) {
			String key = (String) iter.next();
			attributes[attIndex + index] = new RequestPOSTEntry("cond" + key,
					(String) keys.get(key));
		}

		return attributes;
	}

	/**
	 * The {@link FormHandler} used by the FileUploadComponent. After a file is
	 * successfully uploaded to the server, the UploadFormHandler sends an RPC
	 * to the server notifying it that the file has been uploaded and provides
	 * it with the path to the file. The file is then stored in the database.
	 * 
	 * @see Server#storeFile(String, String, com.sinai.mshab.client.ui.Key,
	 *      String)
	 * @author Arthur Kalmenson
	 */
	private class UploadFormHandler implements FormHandler {

		public void onSubmit(FormSubmitEvent event) {
			// do nothing before submitting.
		}

		public void onSubmitComplete(FormSubmitCompleteEvent event) {
			// We receive the path to the file that was uploaded, now we
			// make an async call to the server to store the file in the
			// database.

			String filePath = event.getResults();

			FormData.getInstance().getService().storeFile(fileDBFieldName,
					fileTableName, FormData.getKey(), filePath,
					new UploadCallback());
		}
	}

	/**
	 * The {@link AsyncCallback} for the RPC made by UploadFormHandler. Since we
	 * don't need to update anything in the form, this callback doesn't do much.
	 * 
	 * @author Arthur Kalmenson
	 */
	private class UploadCallback implements AsyncCallback {

		public void onFailure(Throwable caught) {
			GWT.log("Error ", caught);
			caught.printStackTrace();
		}

		// store the given table names.
		public void onSuccess(Object result) {
			System.out.println("Successful uploaded a file");
		}
	}
}
